#include <QTimer>
#include <QtCore/QSettings>
#include <QtGui/QMessageBox>
#include <QtGui/QCloseEvent>

#include <libz800.h>

#include "DlgRawData.h"
#include "DlgGyroCalib.h"
#include "DlgDisplay.h"
#include "DlgProperties.h"
#include "DlgCalibVec.h"
#include "MainWindow.h"

MainWindow::MainWindow(z800_device *dev, QWidget *parent)
: QMainWindow(parent, 0)
, _dev(dev)
, _timer(new QTimer)
, _dlgRawData(new DlgRawData(this))
, _dlgGyroCalib(new DlgGyroCalib(this))
, _dlgDisplay(new DlgDisplay(this))
, _dlgProperties(new DlgProperties(this))
, _mode(0)
, _stereo(false)
{
	_dlgGyroCalib->setDevice(_dev);
	_dlgDisplay->setDevice(_dev);
	_dlgProperties->setDevice(_dev);

	setupUi(this);

	/* Qt 4.7 has a nasty bug on Win32 with intervals any less - an MM timer is used which 
	 * starves the message loop in combination with an active OpenGL context... */

	_timer->setInterval(21);
	_timer->start();

	if(_dev)
	{
		connect(_timer, SIGNAL(timeout()), this, SLOT(update()));
	}

	connect(this, SIGNAL(stabilizationChanged(QQuaternion)), coordDrawer, SLOT(setStabilization(QQuaternion)));
	connect(this, SIGNAL(quatChanged(QQuaternion)), coordDrawer, SLOT(setQuat(QQuaternion)));
	connect(this, SIGNAL(rawDataChanged(QVector3D,QVector3D,QVector3D)), _dlgRawData, SLOT(update(QVector3D,QVector3D,QVector3D)));
	connect(this, SIGNAL(gyroSpeedChanged(QVector3D)), _dlgGyroCalib, SLOT(gyroSpeedChanged(QVector3D)));
	connect(this, SIGNAL(eulerChanged(QVector3D)), _dlgGyroCalib, SLOT(eulerChanged(QVector3D)));
	connect(_dlgDisplay, SIGNAL(stereoModeChanged(bool)), this, SLOT(setStereoEnabled(bool)));

	readSettings();
}

void MainWindow::setStereoEnabled(bool enabled)
{
	if(_stereo == enabled) return;

	_stereo = enabled;
	QGLFormat fmt;
	fmt.setStereo(enabled);
	delete coordDrawer;
	coordDrawer = new CoordDrawer(fmt, this, _mode);
	if(coordDrawer->format().stereo() != enabled)
	{
		if(enabled == false)
		{
			/* This should never happen... */
			qFatal("Failed to disable stereo");
		}
		else
		{
			QMessageBox::critical(NULL,
				"Z800 Calibration Tool",
				"Failed to create stereo OpenGL context");
		}
	}

	verticalLayout->addWidget(coordDrawer);
	connect(this, SIGNAL(stabilizationChanged(QQuaternion)), coordDrawer, SLOT(setStabilization(QQuaternion)));
	connect(this, SIGNAL(quatChanged(QQuaternion)), coordDrawer, SLOT(setQuat(QQuaternion)));

	writeSettings();
}

void MainWindow::update()
{
	if(!z800_poll_tracker(_dev))
	{
		qWarning("z800_poll_tracker() failed");
		return;
	}

	float gyr[3], acc[3], mag[3];
	if(z800_get_calibrated_tracker_data(_dev, &gyr, &acc, &mag))
	{
		emit rawDataChanged(QVector3D(gyr[0], gyr[1], gyr[2]),
				    QVector3D(acc[0], acc[1], acc[2]),
				    QVector3D(mag[0], mag[1], mag[2]));
	}
	else
	{
		qWarning("z800_get_raw_tracker_data() failed");
	}

	float gyrSpeed[3];
	if(z800_get_gyro_speed(_dev, &gyrSpeed))
	{
		emit gyroSpeedChanged(QVector3D(gyrSpeed[0], gyrSpeed[1], gyrSpeed[2]));
	}
	else
	{
		qWarning("z800_get_gyro_speed() failed");
	}

	double q[4];
	if(z800_get_stabilization(_dev, &q))
	{
		emit stabilizationChanged(QQuaternion(q[3], q[0], q[1], q[2]));
	}
	else
	{
		qWarning("z800_get_stabilization() failed");
	}

	if(z800_get_orientation(_dev, &q))
	{
		emit quatChanged(QQuaternion(q[3], q[0], q[1], q[2]));
	}
	else
	{
		qWarning("z800_get_orientation() failed");
	}
}

void MainWindow::on_displayComboBox_currentIndexChanged(int index)
{
	_mode = index;
	coordDrawer->setMode(index);
	writeSettings();
}

void MainWindow::on_actionRaw_Data_triggered()
{
	_dlgRawData->show();
}

void MainWindow::on_actionGyro_Calibration_triggered()
{
	_dlgGyroCalib->show();
}

void MainWindow::on_actionDisplay_triggered()
{
	_dlgDisplay->show();
}

void MainWindow::on_actionEEPROM_triggered()
{
	_dlgProperties->show();
	_dlgProperties->update();
}

void MainWindow::on_actionCalibrateAccMag_triggered()
{
	DlgCalibVec dlgCalibVec;

	dlgCalibVec.setDevice(_dev);

	connect(this, SIGNAL(rawDataChanged(QVector3D,QVector3D,QVector3D)), &dlgCalibVec, SLOT(setRawData(QVector3D,QVector3D,QVector3D)));
	if(dlgCalibVec.exec())
	{
	}
}

void MainWindow::writeSettings()
{
	QSettings settings;

	settings.beginGroup("MainWindow");
	settings.setValue("size", size());
	settings.setValue("pos", pos());
	settings.setValue("mode", _mode);
	settings.setValue("stereo", _stereo);
	settings.endGroup();
}

void MainWindow::readSettings()
{
	QSettings settings;

	settings.beginGroup("MainWindow");
	resize(settings.value("size", QSize(400, 400)).toSize());
	move(settings.value("pos", QPoint(200, 200)).toPoint());
	_mode = settings.value("mode", 0).toInt();
	displayComboBox->setCurrentIndex(_mode);
	coordDrawer->setMode(_mode);
	setStereoEnabled(settings.value("stereo", false).toBool());
	_dlgDisplay->setStereoEnabled(_stereo);
	settings.endGroup();
}

void MainWindow::closeEvent(QCloseEvent *event)
{
	writeSettings();
	event->accept();
}

void MainWindow::keyPressEvent(QKeyEvent *e)
{
	if(e->key() == Qt::Key_Escape)
	{
		close();
	}
}
